home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Skunkware 5
/
Skunkware 5.iso
/
src
/
X11
/
tclMotif-1.4
/
send
/
tclXtSend.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-06-29
|
35KB
|
1,270 lines
/*
* tclXtSend.c --
*
* This file provides procedures that implement the "send"
* command, allowing commands to be passed from interpreter
* to interpreter.
* Status -
* being developed
*
* Copyright 1993 Jan Newmarch, University of Canberra
* Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without
* fee is hereby granted, provided that the above copyright
* notice appear in all copies. The author
* makes no representations about the suitability of this
* software for any purpose. It is provided "as is" without
* express or implied warranty.
*
* Copyright 1989-1992 Regents of the University of California
* Permission to use, copy, modify, and distribute this
* software and its documentation for any purpose and without
* fee is hereby granted, provided that the above copyright
* notice appear in all copies. The University of California
* makes no representations about the suitability of this
* software for any purpose. It is provided "as is" without
* express or implied warranty.
*/
#ifndef lint
static char rcsid[] = "$Header$";
#endif
#include <stdio.h>
#include <stdlib.h>
#include <tcl.h>
#include <X11/Intrinsic.h>
#include <X11/Xatom.h>
#include <X11/Shell.h>
#include <X11/StringDefs.h>
#define TM_MAXARGS 100
/*
* This contains info that is common to all widgets
* created under one display
*/
typedef struct DisplayInfo {
Display *display;
Widget toplevel;
Widget commWidget;
Atom registryProperty;
Atom commProperty;
} DisplayInfo;
/*
* The following structure is used to keep track of the
* interpreters registered by this process.
*/
typedef struct RegisteredInterp {
char *name; /* Interpreter's name (malloc-ed). */
Tcl_Interp *interp; /* Interpreter associated with
* name. */
DisplayInfo *dispPtr; /* Display info associated with name. */
struct RegisteredInterp *nextPtr;
/* Next in list of names associated
* with interps in this process.
* NULL means end of list. */
} RegisteredInterp;
static RegisteredInterp *registry = NULL;
/* List of all interpreters
* registered by this process. */
/*
* When a result is being awaited from a sent command, one of
* the following structures is present on a list of all outstanding
* sent commands. The information in the structure is used to
* process the result when it arrives. You're probably wondering
* how there could ever be multiple outstanding sent commands.
* This could happen if interpreters invoke each other recursively.
* It's unlikely, but possible.
*/
typedef struct PendingCommand {
int serial; /* Serial number expected in
* result. */
char *target; /* Name of interpreter command is
* being sent to. */
Tcl_Interp *interp; /* Interpreter from which the send
* was invoked. */
int code; /* Tcl return code for command
* will be stored here. */
char *result; /* String result for command (malloc'ed).
* NULL means command still pending. */
Boolean timedOut; /* True means timeout proc triggered
* false means it hasn't */
struct PendingCommand *nextPtr;
/* Next in list of all outstanding
* commands. NULL means end of
* list. */
} PendingCommand;
static PendingCommand *pendingCommands = NULL;
/* List of all commands currently
* being waited for. */
/*
* The information below is used for communication between
* processes during "send" commands. Each process keeps a
* private window, never even mapped, with one property,
* "Comm". When a command is sent to an interpreter, the
* command is appended to the comm property of the communication
* window associated with the interp's process. Similarly, when a
* result is returned from a sent command, it is also appended
* to the comm property. In each case, the property information
* is in the form of an ASCII string. The exact syntaxes are:
*
* Command:
* 'C' space window space serial space interpName '|' command '\0'
* The 'C' character indicates that this is a command and not
* a response. Window is the hex identifier for the comm
* window on which to append the response. Serial is a hex
* integer containing an identifying number assigned by the
* sender; it may be used by the sender to sort out concurrent
* responses. InterpName is the ASCII name of the desired
* interpreter, which must not contain any vertical bar characters
* The interpreter name is delimited by a vertical bar (this
* allows the name to include blanks), and is followed by
* the command to execute. The command is terminated by a
* NULL character.
*
* Response:
* 'R' space serial space code space result '\0'
* The 'R' character indicates that this is a response. Serial
* gives the identifier for the command (same value as in the
* command message). The code field is a decimal integer giving
* the Tcl return code from the command, and result is the string
* result. The result is terminated by a NULL character.
*
* The register of interpreters is kept in a property
* "InterpRegistry" on the root window of the display. It is
* organized as a series of zero or more concatenated strings
* (in no particular order), each of the form
* window space name '\0'
* where "window" is the hex id of the comm. window to use to talk
* to an interpreter named "name".
*/
/*
* Maximum size property that can be read at one time by
* this module:
*/
#define MAX_PROP_WORDS 100000
/*
* Forward declarations for procedures defined later in this file:
*/
static int AppendErrorProc _ANSI_ARGS_((Display *display,
XErrorEvent *errorPtr));
static void AppendPropCarefully _ANSI_ARGS_((Display *display,
Window window, Atom property, char *value,
PendingCommand *pendingPtr));
static void DeleteProc _ANSI_ARGS_((ClientData clientData));
static Window LookupName _ANSI_ARGS_((DisplayInfo *dispPtr, char *name,
int delete));
static void SendEventProc _ANSI_ARGS_((Widget w, XtPointer clientData,
XEvent *eventPtr, Boolean *continue_dispatch));
static int SendInit _ANSI_ARGS_((Tcl_Interp *interp, DisplayInfo *dispPtr));
static void TimeoutProc _ANSI_ARGS_((XtPointer clientData,
XtIntervalId *id));
static int SendCmd _ANSI_ARGS_ ((ClientData clientData,
Tcl_Interp *interp, int argc, char **argv));
static int GetInterpNames _ANSI_ARGS_ ((ClientData clientData,
Tcl_Interp *interp, int argc, char **argv));
/*
*--------------------------------------------------------------
*
* NoOpProc -
*
* Does nothing.
*
* Results:
* None
*
* Side effects:
* None
*
*--------------------------------------------------------------
*/
static int
NoOpProc(display, event)
Display *display;
XErrorEvent *event;
{
# ifdef DEBUG
fprintf(stderr, "X error occurred\n");
# endif
}
/*
*--------------------------------------------------------------
*
* TclXtSend_RegisterInterp --
*
* This procedure is called to associate an ASCII name
* with an interpreter. Tm_InitSend must previously
* have been called to set up communication channels
* and specify a display.
*
* Results:
* Zero is returned if the name was registered successfully.
* Non-zero means the name was already in use.
*
* Side effects:
* Registration info is saved, thereby allowing the
* "send" command to be used later to invoke commands
* in the interpreter. The registration will be removed
* automatically when the interpreter is deleted.
*
*--------------------------------------------------------------
*/
int
TclXtSend_RegisterInterp(interp, name, toplevel)
Tcl_Interp *interp; /* Interpreter associated with name. */
char *name; /* The name that will be used to
* refer to the interpreter in later
* "send" commands. Must be globally
* unique. */
Widget toplevel; /* toplevel widget for this
* interp; used to identify display
* for communication. */
{
#define TCL_MAX_NAME_LENGTH 1000
char propInfo[TCL_MAX_NAME_LENGTH + 20];
register RegisteredInterp *riPtr;
Window w;
DisplayInfo *dispPtr;
# ifdef DEBUG
fprintf(stderr, "registering interpeter %s\n", name);
# endif
if (strchr(name, '|') != NULL) {
interp->result =
"interpreter name cannot contain '|' character";
return TCL_ERROR;
}
dispPtr = (DisplayInfo *) XtMalloc(sizeof(DisplayInfo));
dispPtr->commWidget = NULL;
dispPtr->toplevel = toplevel;
dispPtr->display = XtDisplay(toplevel);
if (dispPtr->commWidget == NULL) {
int result;
result = SendInit(interp, dispPtr);
if (result != TCL_OK) {
return result;
}
}
/*
* Make sure the name is unique, and append info about it to
* the registry property. It's important to lock the server
* here to prevent conflicting changes to the registry property.
*/
# ifndef DONT_GRAB_SERVER
XGrabServer(dispPtr->display);
# endif
w = LookupName(dispPtr, name, 0);
if (w != (Window) 0) {
Status status;
int dummyInt;
unsigned int dummyUns;
Window dummyWin;
/*
* The name is currently registered. See if the commWidget
* associated with the name exists. If not, or if the commWidget
* is *our* commWidget, then just unregister the old name (this
* could happen if an application dies without cleaning up the
* registry).
*/
XSetErrorHandler(NoOpProc);
status = XGetGeometry(dispPtr->display, w, &dummyWin, &dummyInt,
&dummyInt, &dummyUns, &dummyUns, &dummyUns, &dummyUns);
XSetErrorHandler(NULL);
if ((status != 0) && (w != XtWindow(dispPtr->commWidget))) {
Tcl_AppendResult(interp, "interpreter name \"", name,
"\" is already in use", (char *) NULL);
XUngrabServer(dispPtr->display);
XFlush(dispPtr->display);
return TCL_ERROR;
}
(void) LookupName(dispPtr, name, 1);
}
sprintf(propInfo, "%x %.*s", XtWindow(dispPtr->commWidget),
TCL_MAX_NAME_LENGTH, name);
XChangeProperty(dispPtr->display,
RootWindow(dispPtr->display, 0),
dispPtr->registryProperty, XA_STRING, 8, PropModeAppend,
(unsigned char *) propInfo, strlen(propInfo)+1);
XUngrabServer(dispPtr->display);
XFlush(dispPtr->display);
/*
* Add an entry in the local registry of names owned by this
* process.
*/
riPtr = (RegisteredInterp *) ckalloc(sizeof(RegisteredInterp));
riPtr->name = (char *) ckalloc((unsigned) (strlen(name) + 1));
strcpy(riPtr->name, name);
riPtr->interp = interp;
riPtr->dispPtr = dispPtr;
riPtr->nextPtr = registry;
registry = riPtr;
/*
* Add the "send" command to this interpreter, and arrange for
* us to be notified when the interpreter is deleted (actually,
* when the "send" command is deleted).
*/
Tcl_CreateCommand(interp, "send", SendCmd, (ClientData) riPtr,
DeleteProc);
Tcl_CreateCommand(interp, "interps", GetInterpNames,
(ClientData) dispPtr, NULL);
# ifdef DEBUG
fprintf(stderr, "Registered interpreter successfully\n");
# endif
return TCL_OK;
}
static void
SendRestrictEvents(app, w, pending)
XtAppContext app;
Widget w;
PendingCommand *pending;
{
XEvent event;
# ifdef DEBUG
fprintf(stderr, "Restricting events\n");
# endif
# ifndef DONT_GRAB_SERVER
XtAddGrab(w, False, False);
# endif
while (pending->result == NULL) {
XtAppNextEvent(app, &event);
XtDispatchEvent(&event);
}
XtRemoveGrab(w);
# ifdef DEBUG
fprintf(stderr, "Finished restricting events\n");
# endif
}
/*
*--------------------------------------------------------------
*
* SendCmd --
*
* This procedure is invoked to process the "send" Tcl command.
* See the user documentation for details on what it does.
*
* Results:
* A standard Tcl result.
*
* Side effects:
* See the user documentation.
*
*--------------------------------------------------------------
*/
static int
SendCmd(clientData, interp, argc, argv)
ClientData clientData; /* Information about sender (only
* dispPtr field is used). */
Tcl_Interp *interp; /* Current interpreter. */
int argc; /* Number of arguments. */
char **argv; /* Argument strings. */
{
RegisteredInterp *senderRiPtr = (RegisteredInterp *) clientData;
Window w;
#define STATIC_PROP_SPACE 100
char *property, staticSpace[STATIC_PROP_SPACE];
int length;
static int serial = 0; /* Running count of sent commands.
* Used to give each command a
* different serial number. */
PendingCommand pending;
XtIntervalId timer;
XtAppContext app;
register RegisteredInterp *riPtr;
char *cmd;
int result;
Bool (*prevRestrictProc)();
char *prevArg;
DisplayInfo *dispPtr = senderRiPtr->dispPtr;
# ifdef DEBUG
fprintf(stderr, "Sending command\n");
# endif
if (dispPtr->commWidget == NULL) {
result = SendInit(interp, dispPtr);
if (result != TCL_OK) {
return result;
}
}
if (argc < 3) {
Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
" interpName arg ?arg ...?\"", (char *) NULL);
return TCL_ERROR;
}
if (argc == 3) {
cmd = argv[2];
} else {
cmd = Tcl_Concat(argc-2, argv+2);
}
# ifdef DEBUG
fprintf(stderr, " command is: %s\n", cmd);
# endif
/*
* See if the target interpreter is local. If so, execute
* the command directly without going through the X server.
* The only tricky thing is passing the result from the target
* interpreter to the invoking interpreter. Watch out: they
* could be the same!
*/
for (riPtr = registry; riPtr != NULL; riPtr = riPtr->nextPtr) {
if (strcmp(riPtr->name, argv[1]) != 0) {
continue;
}
if (interp == riPtr->interp) {
result = Tcl_GlobalEval(interp, cmd);
} else {
result = Tcl_GlobalEval(riPtr->interp, cmd);
interp->result = riPtr->interp->result;
interp->freeProc = riPtr->interp->freeProc;
riPtr->interp->freeProc = 0;
Tcl_ResetResult(riPtr->interp);
}
if (cmd != argv[2]) {
ckfree(cmd);
}
return result;
}
/*
* Bind the interpreter name to a communication window.
*/
w = LookupName(dispPtr, argv[1], 0);
if (w == 0) {
Tcl_AppendResult(interp, "no registered interpeter named \"",
argv[1], "\"", (char *) NULL);
if (cmd != argv[2]) {
ckfree(cmd);
}
return TCL_ERROR;
}
/*
* Register the fact that we're waiting for a command to
* complete (this is needed by SendEventProc and by
* AppendErrorProc to pass back the command's results).
*/
serial++;
pending.serial = serial;
pending.target = argv[1];
pending.interp = interp;
pending.result = NULL;
pending.timedOut = FALSE;
pending.nextPtr = pendingCommands;
pendingCommands = &pending;
/*
* Send the command to target interpreter by appending it to the
* comm window in the communication window.
*/
length = strlen(argv[1]) + strlen(cmd) + 30;
if (length <= STATIC_PROP_SPACE) {
property = staticSpace;
} else {
property = (char *) ckalloc((unsigned) length);
}
sprintf(property, "C %x %x %s|%s",
XtWindow(dispPtr->commWidget), serial, argv[1], cmd);
(void) AppendPropCarefully(dispPtr->display, w, dispPtr->commProperty,
property, &pending);
if (length > STATIC_PROP_SPACE) {
ckfree(property);
}
if (cmd != argv[2]) {
ckfree(cmd);
}
# ifdef DEBUG
fprintf(stderr, "Command sent, awaiting rsponse\n");
# endif
/*
* Enter a loop processing X events until the result comes
* in. If no response is received within a few seconds,
* then timeout. While waiting for a result, look only at
* send-related events (otherwise it would be possible for
* additional input events, such as mouse motion, to cause
* other sends, leading eventually to such a large number
* of nested Tcl_Eval calls that the Tcl interpreter panics).
*/
app = XtWidgetToApplicationContext(dispPtr->commWidget);
timer = XtAppAddTimeOut(app, 5000, TimeoutProc, (XtPointer) &pending);
SendRestrictEvents(app, dispPtr->commWidget, &pending);
if ( ! pending.timedOut) {
XtRemoveTimeOut(timer);
}
/*
* Unregister the information about the pending command
* and return the result.
*/
if (pendingCommands == &pending) {
pendingCommands = pending.nextPtr;
} else {
PendingCommand *pcPtr;
for (pcPtr = pendingCommands; pcPtr != NULL;
pcPtr = pcPtr->nextPtr) {
if (pcPtr->nextPtr == &pending) {
pcPtr->nextPtr = pending.nextPtr;
break;
}
}
}
# ifdef DEBUG
fprintf(stderr, "Send over, result: %s, code: %d\n",
pending.result, pending.code);
# endif
Tcl_SetResult(interp, pending.result, TCL_DYNAMIC);
return pending.code;
}
/*
*----------------------------------------------------------------------
*
* GetInterpNames --
*
* This procedure is invoked to fetch a list of all the
* interpreter names currently registered for the display
* of a particular window.
*
* Results:
* A standard Tcl return value. Interp->result will be set
* to hold a list of all the interpreter names defined for
* tkwin's display. If an error occurs, then TCL_ERROR
* is returned and interp->result will hold an error message.
*
* Side effects:
* None.
*
*----------------------------------------------------------------------
*/
static int
GetInterpNames(clientData, interp, argc, argv)
ClientData clientData;
Tcl_Interp *interp; /* Interpreter for returning a result. */
int argc;
char **argv;
{
DisplayInfo *dispPtr = (DisplayInfo *) clientData;
char *regProp, *separator, *name;
register char *p;
int result, actualFormat;
unsigned long numItems, bytesAfter;
Atom actualType;
/*
* Read the registry property.
*/
regProp = NULL;
result = XGetWindowProperty(dispPtr->display,
RootWindow(dispPtr->display, 0),
dispPtr->registryProperty, 0, MAX_PROP_WORDS,
False, XA_STRING, &actualType, &actualFormat,
&numItems, &bytesAfter, (unsigned char **) ®Prop);
if (actualType == None) {
sprintf(interp->result, "couldn't read intepreter registry property");
return TCL_ERROR;
}
/*
* If the property is improperly formed, then delete it.
*/
if ((result != Success) || (actualFormat != 8)
|| (actualType != XA_STRING)) {
if (regProp != NULL) {
XFree(regProp);
}
sprintf(interp->result, "intepreter registry property is badly formed");
return TCL_ERROR;
}
/*
* Scan all of the names out of the property.
*/
separator = "";
for (p = regProp; (p-regProp) < numItems; p++) {
name = p;
while ((*p != 0) && (!isspace(*p))) {
p++;
}
if (*p != 0) {
name = p+1;
name = Tcl_Merge(1, &name);
Tcl_AppendResult(interp, separator, name, (char *) NULL);
while (*p != 0) {
p++;
}
separator = " ";
}
}
XFree(regProp);
return TCL_OK;
}
/*
*--------------------------------------------------------------
*
* SendInit --
*
* This procedure is called to initialize the
* communication channels for sending commands and
* receiving results.
*
* Results:
* The result is a standard Tcl return value, which is
* normally TCL_OK. If an error occurs then an error
* message is left in interp->result and TCL_ERROR is
* returned.
*
* Side effects:
* Sets up various data structures and windows.
*
*--------------------------------------------------------------
*/
static void ClearInterpCmd(w, cldata, calldata)
Widget w;
XtPointer cldata;
XtPointer calldata;
{
Tcl_DeleteCommand((Tcl_Interp*)cldata,"send");
}
static int
SendInit(interp, dispPtr)
Tcl_Interp *interp; /* Interpreter to use for error
* reporting. */
register DisplayInfo *dispPtr;/* Display to initialize. */
{
Widget parent;
/*
* Get atoms used as property names.
*/
dispPtr->commProperty = XInternAtom(dispPtr->display,
"Comm", False);
dispPtr->registryProperty = XInternAtom(dispPtr->display,
"InterpRegistry", False);
/*
* Create the window used for communication, and set up an
* event handler for it, unless it already exists.
*/
parent = dispPtr->toplevel;
if ((dispPtr->commWidget = XtNameToWidget(parent, "_comm")) != NULL)
return TCL_OK;
dispPtr->commWidget = XtVaCreateWidget("_comm",
transientShellWidgetClass,
parent,
XtNgeometry, "10x10",
XtNoverrideRedirect, TRUE,
NULL);
if (dispPtr->commWidget == NULL) {
return TCL_ERROR;
}
XtRealizeWidget(dispPtr->commWidget);
XtAddEventHandler(dispPtr->commWidget, PropertyChangeMask,
FALSE, SendEventProc, dispPtr);
XtAddCallback(dispPtr->commWidget, XtNdestroyCallback,
ClearInterpCmd,(XtPointer)interp);
return TCL_OK;
}
/*
*--------------------------------------------------------------
*
* LookupName --
*
* Given an interpreter name, see if the name exists in
* the interpreter registry for a particular display.
*
* Results:
* If the given name is registered, return the ID of
* the window associated with the name. If the name
* isn't registered, then return 0.
*
* Side effects:
* If the registry property is improperly formed, then
* it is deleted. If "delete" is non-zero, then if the
* named interpreter is found it is removed from the
* registry property.
*
*--------------------------------------------------------------
*/
static Window
LookupName(dispPtr, name, delete)
register DisplayInfo *dispPtr;
/* Display whose registry to check. */
char *name; /* Name of an interpreter. */
int delete; /* If non-zero, delete info about name. */
{
char *regProp, *entry;
register char *p;
int result, actualFormat;
unsigned long numItems, bytesAfter;
Atom actualType;
Window returnValue;
/*
* Read the registry property.
*/
regProp = NULL;
result = XGetWindowProperty(dispPtr->display,
RootWindow(dispPtr->display, 0),
dispPtr->registryProperty, 0, MAX_PROP_WORDS,
False, XA_STRING, &actualType, &actualFormat,
&numItems, &bytesAfter, (unsigned char **) ®Prop);
if (actualType == None) {
return 0;
}
/*
* If the property is improperly formed, then delete it.
*/
if ((result != Success) || (actualFormat != 8)
|| (actualType != XA_STRING)) {
if (regProp != NULL) {
XFree(regProp);
}
XDeleteProperty(dispPtr->display,
RootWindow(dispPtr->display, 0),
dispPtr->registryProperty);
return 0;
}
/*
* Scan the property for the desired name.
*/
returnValue = (Window) 0;
entry = NULL; /* Not needed, but eliminates compiler warning. */
for (p = regProp; (p-regProp) < numItems; ) {
entry = p;
while ((*p != 0) && (!isspace(*p))) {
p++;
}
if ((*p != 0) && (strcmp(name, p+1) == 0)) {
sscanf(entry, "%x", &returnValue);
break;
}
while (*p != 0) {
p++;
}
p++;
}
/*
* Delete the property, if that is desired (copy down the
* remainder of the registry property to overlay the deleted
* info, then rewrite the property).
*/
if ((delete) && (returnValue != 0)) {
int count;
while (*p != 0) {
p++;
}
p++;
count = numItems - (p-regProp);
if (count > 0) {
memcpy((VOID *) entry, (VOID *) p, count);
}
XChangeProperty(dispPtr->display,
RootWindow(dispPtr->display, 0),
dispPtr->registryProperty, XA_STRING, 8,
PropModeReplace, (unsigned char *) regProp,
(int) (numItems - (p-entry)));
XSync(dispPtr->display, False);
}
XFree(regProp);
return returnValue;
}
/*
*--------------------------------------------------------------
*
* SendEventProc --
*
* This procedure is invoked automatically by the toolkit
* event manager when a property changes on the communication
* window. This procedure reads the property and handles
* command requests and responses.
*
* Results:
* None.
*
* Side effects:
* If there are command requests in the property, they
* are executed. If there are responses in the property,
* their information is saved for the (ostensibly waiting)
* "send" commands. The property is deleted.
*
*--------------------------------------------------------------
*/
static void
SendEventProc(w, clientData, eventPtr, continue_dispatch)
Widget w;
XtPointer clientData; /* Display information. */
XEvent *eventPtr; /* Information about event. */
Boolean *continue_dispatch;
{
DisplayInfo *dispPtr = (DisplayInfo *) clientData;
char *propInfo;
register char *p;
int result, actualFormat;
unsigned long numItems, bytesAfter;
Atom actualType;
# ifdef DEBUG
fprintf(stderr, "Send arriving\n");
# endif
if ((eventPtr->xproperty.atom != dispPtr->commProperty)
|| (eventPtr->xproperty.state != PropertyNewValue)) {
return;
}
/*
* Read the comm property and delete it.
*/
propInfo = NULL;
XSetErrorHandler(NoOpProc);
result = XGetWindowProperty(dispPtr->display,
XtWindow(dispPtr->commWidget),
dispPtr->commProperty, 0, MAX_PROP_WORDS, True,
XA_STRING, &actualType, &actualFormat,
&numItems, &bytesAfter, (unsigned char **) &propInfo);
XSetErrorHandler(NULL);
/*
* If the property doesn't exist or is improperly formed
* then ignore it.
*/
if ((result != Success) || (actualType != XA_STRING)
|| (actualFormat != 8)) {
if (propInfo != NULL) {
XFree(propInfo);
}
# ifdef DEBUG
fprintf(stderr, "bad property format?\n");
# endif
return;
}
/*
* The property is divided into records separated by null
* characters. Each record represents one command request
* or response. Scan through the property one record at a
* time.
*/
# ifdef DEBUG
fprintf(stderr, "Property is: %s\n", propInfo);
# endif
for (p = propInfo; (p-propInfo) < numItems; ) {
if (*p == 'C') {
Window window;
int serial, resultSize;
char *resultString, *interpName, *returnProp, *end;
register RegisteredInterp *riPtr;
char errorMsg[100];
#define STATIC_RESULT_SPACE 100
char staticSpace[STATIC_RESULT_SPACE];
/*
*-----------------------------------------------------
* This is an incoming command sent by another window.
* Parse the fields of the command string. If the command
* string isn't properly formed, send back an error message
* if there's enough well-formed information to generate
* a proper reply; otherwise just ignore the message.
*-----------------------------------------------------
*/
p++;
window = (Window) strtol(p, &end, 16);
if (end == p) {
goto nextRecord;
}
p = end;
if (*p != ' ') {
goto nextRecord;
}
p++;
serial = strtol(p, &end, 16);
if (end == p) {
goto nextRecord;
}
p = end;
if (*p != ' ') {
goto nextRecord;
}
p++;
interpName = p;
while ((*p != 0) && (*p != '|')) {
p++;
}
if (*p != '|') {
result = TCL_ERROR;
resultString = "bad property format for sent command";
goto returnResult;
}
*p = 0;
p++;
/*
* Locate the interpreter for the command, then
* execute the command.
*/
for (riPtr = registry; ; riPtr = riPtr->nextPtr) {
if (riPtr == NULL) {
result = TCL_ERROR;
sprintf(errorMsg,
"receiver never heard of interpreter \"%.40s\"",
interpName);
resultString = errorMsg;
goto returnResult;
}
if (strcmp(riPtr->name, interpName) == 0) {
break;
}
}
# ifdef DEBUG
fprintf(stderr, "Executing sent command %s\n", p);
# endif
result = Tcl_GlobalEval(riPtr->interp, p);
resultString = riPtr->interp->result;
/*
* Return the result to the sender.
*/
returnResult:
resultSize = strlen(resultString) + 30;
if (resultSize <= STATIC_RESULT_SPACE) {
returnProp = staticSpace;
} else {
returnProp = (char *) ckalloc((unsigned) resultSize);
}
sprintf(returnProp, "R %x %d %s", serial, result,
resultString);
# ifdef DEBUG
fprintf(stderr, "returning result: %s\n", returnProp);
# endif
(void) AppendPropCarefully(dispPtr->display, window,
dispPtr->commProperty, returnProp,
(PendingCommand *) NULL);
if (returnProp != staticSpace) {
ckfree(returnProp);
}
} else if (*p == 'R') {
int serial, code;
char *end;
register PendingCommand *pcPtr;
/*
*-----------------------------------------------------
* This record in the property is a result being
* returned for a command sent from here. First
* parse the fields.
*-----------------------------------------------------
*/
# ifdef DEBUG
fprintf(stderr, "Result being returned\n");
# endif
p++;
serial = strtol(p, &end, 16);
if (end == p) {
goto nextRecord;
}
p = end;
if (*p != ' ') {
goto nextRecord;
}
p++;
code = strtol(p, &end, 10);
if (end == p) {
goto nextRecord;
}
p = end;
if (*p != ' ') {
goto nextRecord;
}
p++;
/*
* Give the result information to anyone who's
* waiting for it.
*/
for (pcPtr = pendingCommands; pcPtr != NULL;
pcPtr = pcPtr->nextPtr) {
if ((serial != pcPtr->serial) || (pcPtr->result != NULL)) {
continue;
}
pcPtr->code = code;
pcPtr->result = ckalloc((unsigned) (strlen(p) + 1));
strcpy(pcPtr->result, p);
break;
}
}
nextRecord:
while (*p != 0) {
p++;
}
p++;
}
XFree(propInfo);
# ifdef DEBUG
fprintf(stderr, "Send handled\n");
# endif
}
static PendingCommand *globalPendingPtr; /* hack for poor error handling */
/*
*--------------------------------------------------------------
*
* AppendPropCarefully --
*
* Append a given property to a given window, but set up
* an X error handler so that if the append fails this
* procedure can return an error code rather than having
* Xlib panic.
*
* Results:
* None.
*
* Side effects:
* The given property on the given window is appended to.
* If this operation fails and if pendingPtr is non-NULL,
* then the pending operation is marked as complete with
* an error.
*
*--------------------------------------------------------------
*/
static void
AppendPropCarefully(display, window, property, value, pendingPtr)
Display *display; /* Display on which to operate. */
Window window; /* Window whose property is to
* be modified. */
Atom property; /* Name of property. */
char *value; /* Characters (null-terminated) to
* append to property. */
PendingCommand *pendingPtr; /* Pending command to mark complete
* if an error occurs during the
* property op. NULL means just
* ignore the error. */
{
/* I don't have a full error mechanism going that forms lists
* with client_data like Tk does, so I'll indulge in a grotty
* piece of code: set a global to hold the PendingCommand and
* XSync to force execution of the error handler before anything
* else happens. One day, clean this up
*/
XSetErrorHandler(AppendErrorProc);
globalPendingPtr = pendingPtr;
XChangeProperty(display, window, property, XA_STRING, 8,
PropModeAppend, (unsigned char *) value, strlen(value)+1);
XSync(display, False);
XSetErrorHandler(NULL);
}
/*
* The procedure below is invoked if an error occurs during
* the XChangeProperty operation above.
*/
/* ARGSUSED */
static int
AppendErrorProc(display, errorPtr)
Display *display;
XErrorEvent *errorPtr; /* Information about error. */
{
PendingCommand *pendingPtr = globalPendingPtr;
register PendingCommand *pcPtr;
if (pendingPtr == NULL) {
return 0;
}
/*
* Make sure this command is still pending.
*/
for (pcPtr = pendingCommands; pcPtr != NULL;
pcPtr = pcPtr->nextPtr) {
if ((pcPtr == pendingPtr) && (pcPtr->result == NULL)) {
pcPtr->result = ckalloc((unsigned) (strlen(pcPtr->target) + 50));
sprintf(pcPtr->result,
"send to \"%s\" failed (no communication window)",
pcPtr->target);
pcPtr->code = TCL_ERROR;
break;
}
}
return 0;
}
/*
*--------------------------------------------------------------
*
* TimeoutProc --
*
* This procedure is invoked when too much time has elapsed
* during the processing of a sent command.
*
* Results:
* None.
*
* Side effects:
* Mark the pending command as complete, with an error
* message signalling the timeout.
*
*--------------------------------------------------------------
*/
static void
TimeoutProc(clientData, timer)
XtPointer clientData; /* Information about command that
* has been sent but not yet
* responded to. */
XtIntervalId *timer;
{
PendingCommand *pcPtr = (PendingCommand *) clientData;
register PendingCommand *pcPtr2;
# ifdef DEBUG
fprintf(stderr, "Timer gone off\n");
# endif
/*
* Make sure that the command is still in the pending list
* and that it hasn't already completed. Then register the
* error.
*/
for (pcPtr2 = pendingCommands; pcPtr2 != NULL;
pcPtr2 = pcPtr2->nextPtr) {
static char msg[] = "remote interpreter did not respond";
if ((pcPtr2 != pcPtr) || (pcPtr2->result != NULL)) {
continue;
}
pcPtr2->code = TCL_ERROR;
pcPtr2->result = ckalloc((unsigned) (sizeof(msg) + 1));
strcpy(pcPtr2->result, msg);
pcPtr2->timedOut = TRUE;
return;
}
}
/*
*--------------------------------------------------------------
*
* DeleteProc --
*
* This procedure is invoked by Tcl when a registered
* interpreter is about to be deleted. It unregisters
* the interpreter.
*
* Results:
* None.
*
* Side effects:
* The interpreter given by riPtr is unregistered.
*
*--------------------------------------------------------------
*/
static void
DeleteProc(clientData)
ClientData clientData; /* Info about registration, passed
* as ClientData. */
{
RegisteredInterp *riPtr = (RegisteredInterp *) clientData;
register RegisteredInterp *riPtr2;
# ifndef DONT_GRAB_SERVER
XGrabServer(riPtr->dispPtr->display);
# endif
(void) LookupName(riPtr->dispPtr, riPtr->name, 1);
XUngrabServer(riPtr->dispPtr->display);
XFlush(riPtr->dispPtr->display);
if (registry == riPtr) {
registry = riPtr->nextPtr;
} else {
for (riPtr2 = registry; riPtr2 != NULL;
riPtr2 = riPtr2->nextPtr) {
if (riPtr2->nextPtr == riPtr) {
riPtr2->nextPtr = riPtr->nextPtr;
break;
}
}
}
Tcl_DeleteCommand(riPtr->interp,"interps");
if(NULL!=riPtr->dispPtr->commWidget)
XtRemoveCallback(riPtr->dispPtr->commWidget,XtNdestroyCallback,
ClearInterpCmd,riPtr->interp);
ckfree((char *) riPtr->name);
ckfree((char *) riPtr);
}